//
// Unihan Input Method
//
// Copyright © 2002-2007 Sun Wah Linux Ltd.
//
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
// USA
//
// $Id: unihan2.c 820 2007-08-29 08:30:45Z roger $
//
/// @file unihan2.c
/// @brief Basic htt language engine implementation.

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <iml/SunIM.h>

#define LOG_DOMAIN "core"
#include "core.h"
#include "keyhandler.h"
#include "ui.h"
#include "unihan_xaux.h"
#include "im_methods.h"
#include "phrase.h"
#include "unicode.h"
#include "log.h"

#define UNIHAN_LE_VERSION "2.0"

/***********************************
** LE handlers
*/
void if_GetIfInfo(IMArgList args, int num_args);
Bool unihan_open_if(iml_if);
Bool unihan_close_if(iml_if);
Bool unihan_get_if_values(iml_if, IMArgList, int);
Bool unihan_set_if_values(iml_if, IMArgList, int);
Bool unihan_open_desktop(iml_desktop_t *, IMArgList, int);
Bool unihan_close_desktop(iml_desktop_t *);
Bool unihan_create_sc(iml_session, IMArgList, int);
Bool unihan_destroy_sc(iml_session);
Bool unihan_get_sc_values(iml_session, IMArgList, int);
Bool unihan_set_sc_values(iml_session, IMArgList, int);
IMText *unihan_reset_sc(iml_session);
void unihan_set_sc_focus(iml_session);
void unihan_unset_sc_focus(iml_session);
void unihan_handle_event(iml_session, IMInputEvent *);

static void unihan_handle_aux_event(iml_session_t *, IMAuxEvent *);
static void commit_string_u16(iml_session_t *s, UTFCHAR *u16, int len);

static if_methods_t methods_table = {
	.if_OpenIF       = unihan_open_if,
	.if_CloseIF      = unihan_close_if,
	.if_GetIFValues  = unihan_get_if_values,
	.if_SetIFValues  = unihan_set_if_values,
	.if_OpenDesktop  = unihan_open_desktop,
	.if_CloseDesktop = unihan_close_desktop,
	.if_CreateSC     = unihan_create_sc,
	.if_DestroySC    = unihan_destroy_sc,
	.if_GetSCValues  = unihan_get_sc_values,
	.if_SetSCValues  = unihan_set_sc_values,
	.if_ResetSC      = unihan_reset_sc,
	.if_SetSCFocus   = unihan_set_sc_focus,
	.if_UnsetSCFocus = unihan_unset_sc_focus,
	.if_SendEvent    = unihan_handle_event
};

static UTFCHAR hrn_string_en[] = {'E', 'n', 'g', 'l', 'i', 's', 'h'};
static UTFCHAR hrn_string_zhhk[] = {0x9999, 0x6e2f, 0x4e2d, 0x6587, 0};
static UTFCHAR hrn_string_zhtw[] = {0x7e41, 0x9ad4, 0x4e2d, 0x6587, 0};
static UTFCHAR hrn_string_zhcn[] = {0x7b80, 0x4f53, 0x4e2d, 0x6587, 0};
UTFCHAR lename_string[] = {0x6f22, 0x7d71, 0};
UTFCHAR aux_name[] = {0x6f22, 0x7d71, '2', 'A', 'u', 'x', 0};

/* 
** NOTE: this must be the same as the .so name.
**       how stupid -__-;;
*/
static IMLEName lename = {
	"unihan2", lename_string
};

/*
** NOTE: the locale here MUST be supported by htt_server
*/
static IMLocale locales[] = {
	{"en",    hrn_string_en},
	{"zh_CN", hrn_string_zhcn},
	{"zh_HK", hrn_string_zhhk},
	{"zh_TW", hrn_string_zhtw},
	{NULL, NULL}
};

static char *class_names[] = {
        AUX_STATUS_CLASS_NAME,
        AUX_LOOKUP_CLASS_NAME,
};

/*There is a stupid method to resolve the problem with using mouse to swith focus
 *the next two variables are used for the purpose
 *Forgive me , it is not all my fault , the iiimf server send signal in uncertain order
 */
int set_sc_focus_before=0;   
int no_erase=0;     


/***************************************************************************/
void
if_GetIfInfo(IMArgList args, int num_args)
{
    unihan_log_init();
	int i;
        IMObjectDescriptorStruct *objects = NULL;

	for (i = 0; i < num_args; i++, args++) {
		switch (args->id) {
			case IF_VERSION:
				args->value = (IMArgVal)UNIHAN_LE_VERSION;
				break;
			case IF_METHOD_TABLE:
				args->value = (IMArgVal)&methods_table;
				break;
			case IF_LE_NAME:
				args->value = (IMArgVal)&lename;
				break;
			case IF_SUPPORTED_LOCALES:
				args->value = (IMArgVal)&locales;
				break;
			case IF_SUPPORTED_OBJECTS:
                objects = (IMObjectDescriptorStruct *)
                            calloc(2, sizeof(IMObjectDescriptorStruct));
                objects[0].leid = "unihan2";
                objects[0].type = IM_DOWNLOADINGOBJECT_BINGUI_TYPE;
                objects[0].name = aux_name;
                objects[0].name_length = 6;
                objects[0].domain = "com.sw-linux";
                objects[0].scope = "unihan2";
                objects[0].path = "unihan2/xaux_gnome.so";
                objects[0].signature = "";
                objects[0].class_names = class_names;
                objects[0].count_names = 2;
				args->value = (IMArgVal)objects;
				break;
			case IF_NEED_THREAD_LOCK:
				args->value = (IMArgVal)False;
				break;
			case IF_SUPPORTED_KEYRELEASE:
				args->value = (IMArgVal)True;
				break;
			default:
                unihan_debug("getting unknown IF info: id = %d", args->id);
				break;
		}
	}
}


IMHotkeyManagerStruct *
if_GetHotKeyInfo(IMLEName *le_name)
{
    unihan_log_init();
    unihan_debug("getting hotkeys...");
    return NULL;

	// FIXME: memory leak!
	IMHotkeyManagerStruct *hkm = (IMHotkeyManagerStruct *)calloc(1, sizeof(IMHotkeyManagerStruct));
	hkm->num_hotkey_profiles = 1;
	hkm->hkps = (IMHotkeyProfileStruct *)calloc(1, sizeof(IMHotkeyProfileStruct));
	hkm->hkps[0].name = le_name;
	hkm->hkps[0].profile_id = 3000;
	hkm->hkps[0].scope = SESSION_SPECIFIC_HOTKEY;
	hkm->hkps[0].num_hotkeys = 3;
	hkm->hkps[0].hks = (IMHotkeyStruct *)calloc(3, sizeof(IMHotkeyStruct));
	hkm->hkps[0].hks[0].label = strdup("Trigger key");
	hkm->hkps[0].hks[0].state_flag = 0;
	hkm->hkps[0].hks[0].action_flag = 1;
	hkm->hkps[0].hks[0].nkeys = 1;
	hkm->hkps[0].hks[0].keys = (IMKeyEventStruct *)calloc(1, sizeof(IMKeyEventStruct));
	hkm->hkps[0].hks[0].keys[0].keyCode = IM_VK_SPACE;
	hkm->hkps[0].hks[0].keys[0].modifier = IM_CTRL_MASK;
	hkm->hkps[0].hks[1].label = strdup("Next IM");
	hkm->hkps[0].hks[1].state_flag = 0;
	hkm->hkps[0].hks[1].action_flag = 1;
	hkm->hkps[0].hks[1].nkeys = 1;
	hkm->hkps[0].hks[1].keys = (IMKeyEventStruct *)calloc(1, sizeof(IMKeyEventStruct));
	hkm->hkps[0].hks[1].keys[0].keyCode = IM_VK_SHIFT;
	hkm->hkps[0].hks[1].keys[0].modifier = IM_CTRL_MASK;
	hkm->hkps[0].hks[2].label = strdup("Previous IM");
	hkm->hkps[0].hks[2].state_flag = 0;
	hkm->hkps[0].hks[2].action_flag = 1;
	hkm->hkps[0].hks[2].nkeys = 1;
	hkm->hkps[0].hks[2].keys = (IMKeyEventStruct *)calloc(1, sizeof(IMKeyEventStruct));
	hkm->hkps[0].hks[2].keys[0].keyCode = IM_VK_CONTROL;
	hkm->hkps[0].hks[2].keys[0].modifier = IM_SHIFT_MASK;

	return hkm;
}


/***************************************************************************/
Bool
unihan_open_if(iml_if le)
{
    unihan_log_init();
	unihan_debug("unihan_open_if()");
	unihan_debug("\tle->if_name = %s", le->if_name);
	unihan_debug("\tle->if_version = %s", le->if_version);
	unihan_debug("\tle->locale = %s", le->locale);
	unihan_debug("\tle->ifpath_name = %s", le->ifpath_name);
	unihan_debug("\tle->desktop_count = %d", le->desktop_count);
	return True;
}


/***************************************************************************/
Bool
unihan_close_if(iml_if le)
{
	unihan_debug("unihan_close_if()");
	unihan_debug("\tle->desktop_count = %d", le->desktop_count);

	/* what should we return? */
	return True;
}


/***************************************************************************/
Bool 
unihan_get_if_values(iml_if le, IMArgList args, int num_args)
{
	/*
	** "these methods are to query and set IF attrib. However, there is no
	**  usable IF values in this IM-SDK release..."
	*/
    int i = 0;
	for (i = 0; i < num_args; i++, args++) {
        unihan_debug("getting unknown IF value: id = %d", args->id);
    }
	return True;
}


/***************************************************************************/
Bool 
unihan_set_if_values(iml_if le, IMArgList args, int num_args)
{
    int i = 0;
	for (i = 0; i < num_args; i++, args++) {
        unihan_debug("setting unknown IF value: id = %d", args->id);
    }
	return True;
}


/***************************************************************************/
Bool 
unihan_open_desktop(iml_desktop_t *desktop, IMArgList args, int num_args)
{
	DataPerDesktop *dd;
	int i;
    int numIM;
	
	unihan_debug("unihan_open_desktop()");

    dd = (DataPerDesktop *)malloc(sizeof(DataPerDesktop));
	if (!dd)
		return False;
	memset(dd, 0, sizeof(DataPerDesktop));
	
	for (i = 0; i < num_args; i++, args++) {
		switch (args->id) {
			case UI_USER_NAME:
				unihan_debug("\tuser_name = %s", args->value);
                dd->username = strdup(args->value);
				break;
			case UI_HOST_NAME:
				unihan_debug("\thost_name = %s", args->value);
                dd->hostname = strdup(args->value);
				break;
			case UI_DISPLAY_ID:
				unihan_debug("\tdisplay_id = %s", args->value);
                dd->display_id = strdup(args->value);
				break;
			case UI_PROTOCOL_TYPE:
				unihan_debug("\tprotocol_type = %s", args->value);
                dd->protocol_type = strdup(args->value);
				break;
			case UI_CLIENT_TYPE:
				unihan_debug("\tclient_type = %s", args->value);
                dd->client_type = strdup(args->value);
				break;
			case UI_OS_NAME:
				unihan_debug("\tos_name = %s", args->value);
                dd->os_name = strdup(args->value);
				break;
			case UI_OS_ARCH:
				unihan_debug("\tos_arch = %s", args->value);
                dd->os_arch = strdup(args->value);
				break;
			case UI_OS_VERSION:
				unihan_debug("\tos_version = %s", args->value);
                dd->os_version = strdup(args->value);
				break;
            case UI_XSERVER_VENDOR:
				unihan_debug("\txserver_vendor = %s", args->value);
                break;
            case UI_AUTH_PASSWD:
				unihan_debug("\tauth_passwd = %s", args->value);
                break;
            default:
                unihan_debug("\tunknown desktop value: id = %d", args->id);
		}
	}

    dd->aux_started = 0;
    dd->phrase_mode = 0;
	desktop->specific_data = (void *)dd;

#if TIMEBOMB
        check_expired(desktop);
#endif

	keyhandler_init(desktop);
	im_manager_init(desktop);
	phrase_matcher_init(desktop);

        numIM = im_manager_get_num_im(desktop);
	if (numIM > 0) {
                dd->imConfig = (IMConfigList **)malloc(
				sizeof(IMConfigList *) * numIM);
                memset(dd->imConfig, 0, sizeof(IMConfigList *) * numIM);


		/* return True if desktop allowed to open */
		return True;
        }

	phrase_matcher_done(desktop);
	im_manager_done(desktop);
	keyhandler_done(desktop);
	if (desktop->specific_data)
		free(desktop->specific_data);
	return False;
}

/***************************************************************************/
Bool
unihan_close_desktop(iml_desktop_t *desktop)
{
	unihan_debug("unihan_close_desktop()");

    DataPerDesktop *dd = desktop->specific_data;
        
	phrase_matcher_done(desktop);
	im_manager_done(desktop);
	keyhandler_done(desktop);

    if (dd) {
        if (dd->username)       free (dd->username);
        if (dd->hostname)       free (dd->hostname);
        if (dd->protocol_type)  free (dd->protocol_type);
        if (dd->client_type)    free (dd->client_type);
        if (dd->display_id)     free (dd->display_id);
        if (dd->os_name)        free (dd->os_name);
        if (dd->os_version)     free (dd->os_version);
        if (dd->os_arch)        free (dd->os_arch);
        if (dd->imConfig)       free (dd->imConfig);
        if (desktop->specific_data) free(desktop->specific_data);
    }
	return True;
}

/***************************************************************************/
Bool
unihan_create_sc(iml_session s, IMArgList args, int num_args)
{
    unihan_debug("unihan_create_sc()");

	DataPerSession *ds;
	//DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	int numIM;

	// Note: args and num_args are the same as the ones given in 
	//       open_desktop().

	ds = (DataPerSession *)s->If->m->iml_new2(s, sizeof(DataPerSession));
	
	if (!ds)
		return False;
	memset(ds, 0, sizeof(DataPerSession));

	numIM = im_manager_get_num_im(s->desktop);

	if (numIM > 0) {
		ds->userData = (UserDataTable *)malloc(sizeof(UserDataTable)*numIM);
		memset(ds->userData, 0, sizeof(UserDataTable) * numIM);
	} else {
		// hey, no IM is available, so why bother?
		return False;	// we don't want the session to be created
	}

	s->specific_data = (void *)ds;

	im_manager_create_session(s);
	phrase_matcher_create_session(s);

	return True;
}


/***************************************************************************/
Bool
unihan_destroy_sc(iml_session session)
{
    unihan_debug("unihan_destroy_sc()");

	phrase_matcher_destroy_session(session);
	im_manager_destroy_session(session);

	DataPerSession *ds = (DataPerSession *)session->specific_data;
    if (ds) {
        if (ds->userData) free(ds->userData);
        if (ds->locale) free(ds->locale);
    }

	return True;
}


/***************************************************************************/
Bool
unihan_get_sc_values(iml_session session, IMArgList args, int num_args)
{
    unihan_debug("unihan_get_sc_values()");

	int i;
	static int charsubset[] = {
		67, 		/* Latin */
		47, 		/* Hiragana */
		48,		/* Katakana */
		69,		/* Traditional Chinese */
		70,		/* Simplified Chinese */
		0
	};

	for (i = 0; i < num_args; i++, args++) {
		switch (args->id) {
			case SC_SUPPORTED_CHARACTER_SUBSETS:
				args->value = (IMArgVal)charsubset;
				break;
			default:
                unihan_debug("getting unknown SC value: id = %d", args->id);
				break;
		}
	}

	return True;
}


/***************************************************************************/
Bool
unihan_set_sc_values(iml_session session, IMArgList args, int num_args)
{
    unihan_debug("unihan_set_sc_values()");

	int i;
	DataPerSession *ds = (DataPerSession *)session->specific_data;
	//DataPerDesktop *dd = (DataPerDesktop *)session->desktop->specific_data;

	for (i = 0; i < num_args; i++, args++) {
		switch (args->id) {
			case SC_TRIGGER_ON_NOTIFY:
                unihan_debug("SC_TRIGGER_ON_NOTIFY");
				conversion_on(session);
				no_erase=0;
				set_sc_focus_before=1;
				break;

			case SC_TRIGGER_OFF_NOTIFY:
                unihan_debug("SC_TRIGGER_OFF_NOTIFY");
				if (ds->phrase_state == PHRASE_STATE_SELECTING) {
					ds->phrase_state = PHRASE_STATE_IDLE;
					phrase_matcher_hide_results(session);
				}
				conversion_off(session);
				break;

			case SC_REALIZE:
                unihan_debug("SC_REALIZE");
			#if 0
				if (!dd->lookup_aux_started)
                                        aux_start(session, AUX_LOOKUP_CLASS_NAME);
                                if (!dd->status_aux_started)
                                        aux_start(session, AUX_STATUS_CLASS_NAME);
			#endif
				aux_start(session, AUX_LOOKUP_CLASS_NAME);
				aux_start(session, AUX_STATUS_CLASS_NAME);
				status_update(session);
				
#if TIMEBOMB
                                if (check_expired(session->desktop)) {
                                        notify_expired(session);
                                } else {
                                        notify_timebomb(session);
                                }
#endif
				break;

            case SC_CLIENT_LOCALE:
                unihan_debug("client_locale = %s", args->value);
                ds->locale = strdup(args->value);
                break;
            case SC_CLIENT_INPUT_METHOD_NAME:
                unihan_debug("input_method_name = %s", args->value);
                break;
            case SC_CLIENT_CHARACTER_SUBSETS:
                unihan_debug("character_subsets = %d", args->value);
                break;
			default:
                unihan_debug("setting unknown SC value: id = %d", args->id);
				break;
		}
	}

	return True;
}

/***************************************************************************/
void
unihan_set_sc_focus(iml_session session) 
{
    unihan_debug("unihan_set_sc_focus()");

	DataPerSession *ds = (DataPerSession *)session->specific_data;
    DataPerDesktop *dd = (DataPerDesktop *)session->desktop->specific_data;
	
	if ( set_sc_focus_before )
		no_erase=1;
	set_sc_focus_before=1;
		
#if 0
	/* let's switch hotkey profile */
	iml_hkc *hkc;
	iml_hkc_create_t le_hkc_create;
	iml_hkc_free_t le_hkc_free;
	le_hkc_create = session->desktop->If->hkc_get_function("_hkc_create");
	le_hkc_free = session->desktop->If->hkc_get_function("_hkc_free");
	hkc = le_hkc_create("unihan2", IML_HKC_TYPE_DESKTOP, session->desktop);
	session->desktop->If->switchLEProfile(session, 3000, &lename);
	le_hkc_free(hkc);
#endif

#if NEW_PROPERTY
#else
	check_fullwidth(session);
	check_puncce(session);
#endif

        if (ds->punctuation)
                punc_keyboard_show(session);
	if (ds->us_kbd)
		us_keyboard_show(session);
    if (dd->phrase_mode == 0 && ds->phrase_state != PHRASE_STATE_NEVER)
        ds->phrase_state = PHRASE_STATE_NEVER;
    else if (dd->phrase_mode == 1 && ds->phrase_state == PHRASE_STATE_NEVER)
        ds->phrase_state = PHRASE_STATE_IDLE;
	if (ds->phrase_state == PHRASE_STATE_SELECTING)
		phrase_matcher_show_results(session);

	im_propagate_focus_event(session, True);
}


/***************************************************************************/
void
unihan_unset_sc_focus(iml_session session) 
{
    unihan_debug("unihan_unset_sc_focus()");

    DataPerSession *ds = (DataPerSession *)session->specific_data;
        
#if NEW_PROPERTY
#else
	check_fullwidth(session);
	check_puncce(session);
#endif

	if ( !no_erase )
		im_propagate_focus_event(session, False);
	no_erase=0;
	set_sc_focus_before=0;

	if (ds->punctuation)
		punc_keyboard_hide(session);

	if (ds->us_kbd)
		us_keyboard_hide(session);

	if (ds->phrase_state == PHRASE_STATE_SELECTING)
		phrase_matcher_hide_results(session);
}


/***************************************************************************/
IMText *
unihan_reset_sc(iml_session session) 
{
    unihan_debug("unihan_reset_sc()");

	return NULL;
}


/***************************************************************************/
void
unihan_handle_event(iml_session session, IMInputEvent *ev)
{
    unihan_debug("unihan_handle_event()");

	switch (ev->type) {
		case IM_EventKeyList:
			unihan_handle_key_event(session, (IMKeyListEvent*)ev);
            break;
		case IM_HotkeyEvent:
			unihan_handle_hotkey_event(session, (IMKeyListEvent*)ev);
			break;
		case IM_EventAux:
			unihan_handle_aux_event(session, (IMAuxEvent*)ev);
			break;
		default:
			break;
	}
}


/***************************************************************************/
static void
unihan_handle_aux_event(iml_session_t *session, IMAuxEvent *ev)
{
        DataPerSession *ds = (DataPerSession *)session->specific_data;
        DataPerDesktop *dd = (DataPerDesktop *)session->desktop->specific_data;
	// propagate event to current IM?
        IMAuxDrawCallbackStruct *aux = ev->aux;
        UTF8CHAR *tmp = NULL;
        UTFCHAR *tmpu16 = NULL;

        if (aux->count_integer_values < 1)
                return;

        switch (aux->integer_values[0]) {
	case UNIHAN_ENGINE_GET_IM_LIST:
		im_manager_get_im_list(session, aux);
		break;

#if 0
	case UNIHAN_ENGINE_IM_SET_PROPS: {
		int i;
		UnihanPropertyArgList plist;

		if (aux->count_string_values < 2)
			return;

		for(i=1; i<aux->count_string_values; i++) {
			tmpu16 = (UTFCHAR*)malloc(sizeof(UTFCHAR) *
					(aux->string_values[i].char_length + 1));
                       	memset(tmpu16,
					0,
					sizeof(UTFCHAR) *
					(aux->string_values[i].char_length + 1));
			memcpy(tmpu16,
					aux->string_values[i].text.utf_chars,
					sizeof(UTFCHAR)*aux->string_values[i].char_length);
                       	tmp = utf16be_to_utf8(s, tmpu16, NULL);

                       	convert_string_to_props(aux->integer_values[i], tmp, &plist);
                       	free(tmpu16);
		}
		im_update_property_list(session, plist);
		break;
	}
#endif 

	case UNIHAN_AUX_STATUS_START_NOTIFY:
        dd->aux_started |= STATUS_STARTED;
		break;

	case UNIHAN_AUX_LOOKUP_START_NOTIFY:
        dd->aux_started |= LOOKUP_STARTED;
		break;

#if NEW_PROPERTY

	case UNIHAN_ENGINE_PROPERTY_TOGGLED: {
		if (aux->count_string_values < 1)
			return;

		
		tmpu16 = (UTFCHAR*)malloc(sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memset(tmpu16, 0, sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memcpy(tmpu16, aux->string_values[0].text.utf_chars,
				sizeof(UTFCHAR)*aux->string_values[0].char_length);
		tmp = utf16be_to_utf8(session, tmpu16, NULL);
		toggle_property(session, tmp);
		free(tmpu16);
		break;
	}
	
	case UNIHAN_ENGINE_PROPERTY_REGISTER: {
		if (aux->count_string_values < 1)
			return;
		
		if (ds->conv_on != 1) {
			ds->conv_on = 1;
		}

		tmpu16 = (UTFCHAR*)malloc(sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memset(tmpu16, 0, sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memcpy(tmpu16, aux->string_values[0].text.utf_chars,
				sizeof(UTFCHAR)*aux->string_values[0].char_length);
		tmp = utf16be_to_utf8(session, tmpu16, NULL);
		im_change_to_im(session, tmp);
		register_im_property(session, tmp);
		free(tmpu16);
		break;
	}
	
#endif

	case UNIHAN_ENGINE_SET_TCSC_MODE: {
		// FIXME: should check whether client encoding can
		// handle TC/SC conversion
		if (aux->count_integer_values < 2)
			return;
		int tcsc;

		tcsc = (UnihanTCSCMode)aux->integer_values[1];
		tcsc_set(session, tcsc);

#if 0
		UnihanPropertyArgList prop = NULL;
		prop = im_get_runtime_property(session, UNIHAN_RUNTIME_PROPERTY_TCSC, 0);
		if(prop->type)
    			prop->UnihanProperty.rt_property.value = tcsc;
#endif
		break;
	}

	case UNIHAN_ENGINE_SET_PHRASE_MODE: 
		if (aux->count_integer_values < 2)
			return;
		{
			int phrase = aux->integer_values[1];
			if (1 == phrase) {
				ds->phrase_state = PHRASE_STATE_IDLE;
                dd->phrase_mode = 1;
			} else {
				ds->phrase_state = PHRASE_STATE_NEVER;
                dd->phrase_mode = 0;
			}
		}
		break;
	
	case UNIHAN_ENGINE_SWITCH_IM:
		if (aux->count_string_values < 1)
			return;

		if (ds->conv_on != 1) {

#if NEW_PROPERTY
			ds->conv_on = 1;
#else
			conversion_on(session);
			no_erase=0;
			set_sc_focus_before=1;
#endif
		}
		tmpu16 = (UTFCHAR*)malloc(sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memset(tmpu16, 0, sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memcpy(tmpu16, aux->string_values[0].text.utf_chars,
				sizeof(UTFCHAR)*aux->string_values[0].char_length);
		tmp = utf16be_to_utf8(session, tmpu16, NULL);
		im_change_to_im(session, (char *)tmp);
		free(tmpu16);
		break;

	case UNIHAN_ENGINE_SWITCH_NEXT_IM:
		unihan_debug("received UNIHAN_ENGINE_SWITCH_NEXT_IM event");
		im_change_to_next_im1(session);
		break;

	case UNIHAN_ENGINE_SWITCH_PREV_IM:
		unihan_debug("received UNIHAN_ENGINE_SWITCH_PREV_IM event");
		im_change_to_prev_im1(session);
		break;

	case UNIHAN_ENGINE_SET_DEFAULT_IM:
		if (aux->count_string_values < 1)
			return;

		tmpu16 = (UTFCHAR*)malloc(sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memset(tmpu16, 0, sizeof(UTFCHAR) *
				(aux->string_values[0].char_length + 1));
		memcpy(tmpu16, aux->string_values[0].text.utf_chars,
				sizeof(UTFCHAR)*aux->string_values[0].char_length);
		tmp = utf16be_to_utf8(session, tmpu16, NULL);
		im_manager_set_default_im(session, (char *)tmp);

		free(tmpu16);
		break;

	case UNIHAN_ENGINE_COMMIT:
		if (aux->count_string_values < 1)
			return;

#if TIMEBOMB
		if (check_expired(s->desktop)) {
			notify_expired(session);
			return;
		}
#endif
		commit_string_u16(session,
				aux->string_values[0].text.utf_chars,
				aux->string_values[0].char_length);
		break;

	case UNIHAN_ENGINE_US_KEYBOARD: {
		IMKeyListEvent kev;
		IMKeyEventStruct kes;
		IMKeyList keylist = kev.keylist = &kes;

		if (aux->count_string_values < 1)
			return;

		kev.type = IM_KEY;
		kev.n_key = 1;
		if ((aux->string_values[0].text.native_chars[0] >= 'a') && (aux->string_values[0].text.native_chars[0] <= 'z'))
			keylist->keyCode = IM_VK_A + aux->string_values[0].text.native_chars[0] - 'a';
		else
			keylist->keyCode = aux->string_values[0].text.native_chars[0];
		keylist->keyChar = aux->string_values[0].text.native_chars[0];
		keylist->modifier = 0;

#if TIMEBOMB
		if (check_expired(s->desktop)) {
			notify_expired(session);
			return;
		}
#endif
		if (ds->conv_on != 0)
			unihan_handle_key_event(session, &kev);
		else
			 commit_string_u16(session,
							   aux->string_values[0].text.utf_chars,
							   aux->string_values[0].char_length);
		break;
	} 

	case UNIHAN_ENGINE_US_KEYBOARD_SET_STATE:
		if (aux->count_integer_values < 3)
			return;
		ds->us_kbd_caps_pressed = aux->integer_values[1];
		ds->us_kbd_shift_pressed = aux->integer_values[2];
		break;

	/* half/full shape */
	case UNIHAN_ENGINE_TOGGLE_WIDTH: 	
		toggle_width(session);
		break;
	case UNIHAN_ENGINE_CHECK_MODULE_INFO:
		check_module_info(session);
		break;

	/* english/chinese punctuation */
	/* 2004.2.11 arren */
	case UNIHAN_ENGINE_TOGGLE_PUNCCE:
		toggle_puncce(session);
		break;

	case UNIHAN_ENGINE_IM_CONFIG_CHANGE: {
		char *utf8[3];
		int i;
		if (aux->count_string_values < 3)
			return;

		for (i = 0; i < 3; i++) {
			tmpu16 = (UTFCHAR*)malloc(
				sizeof(UTFCHAR) *
				(aux->string_values[i].char_length + 1));
			if (!tmpu16)
				return;
			memset(tmpu16,
				0,
				sizeof(UTFCHAR) *
				(aux->string_values[i].char_length + 1));
			memcpy(tmpu16,
				aux->string_values[i].text.utf_chars,
				sizeof(UTFCHAR)*aux->string_values[i].char_length);
			utf8[i] = (char *)utf16be_to_utf8(NULL, tmpu16, NULL);
			free(tmpu16);
		}
		im_config_change(session, utf8[0], utf8[1], utf8[2]);
		free(utf8[0]);
		break;
	}
	case UNIHAN_ENGINE_TOGGLE_PUNC:
		toggle_punctuation_keyboard(session); 
		break;

	case UNIHAN_ENGINE_TOGGLE_US:
		toggle_us_keyboard(session);
		break;

	case UNIHAN_ENGINE_CONV_OFF:
		conversion_off(session);
		break;

	case UNIHAN_ENGINE_SET_SYMBOL_LIST: {
		char **utf8 = NULL;
		SymbolUserTable *sut = NULL;
		int count_values = aux->count_string_values;
		int numSut = count_values / 3;
		int i;

		if (count_values) {
			utf8 = (char **)malloc(sizeof(char **) *
					count_values + 1);
			memset(utf8, 0, sizeof(char **) *
					count_values + 1);
		}

		for (i = 0; i < count_values; i++) {
			tmpu16 = (UTFCHAR*)malloc(
				sizeof(UTFCHAR) *
				(aux->string_values[i].char_length + 1));
			if (!tmpu16)
				return;
			memset(tmpu16,
				0,
				sizeof(UTFCHAR) *
				(aux->string_values[i].char_length + 1));
			memcpy(tmpu16,
				aux->string_values[i].text.utf_chars,
				sizeof(UTFCHAR)*aux->string_values[i].char_length);
			utf8[i] = (char *)utf16be_to_utf8(NULL, tmpu16, NULL);
			free(tmpu16);
		}

		if (numSut) {
			sut = (SymbolUserTable *)malloc(numSut *
					sizeof(SymbolUserTable));
			memset(sut, 0, numSut *
					sizeof(SymbolUserTable));
		}
		for (i=0; i<numSut; i++) {
			sut[i].modifier = atoi(utf8[i*3]);
			sut[i].keyCode = atoi(utf8[i*3 + 1]);
			sut[i].symbol = utf8[i*3 + 2];
		}

		i--;
		for (; i>=0; i--) {
			free(utf8[i*3]);
			free(utf8[i*3 +1]);
		}
		if (utf8)
			free(utf8);
		im_set_symbol_user_table(session, numSut, sut);
		break;
	}
#if 0
	case UNIHAN_ENGINE_SET_WORD_LIST: {
		char **utf8 = NULL;
		WordUserTable *wut = NULL;
		int count_values = aux->count_string_values;
		int numWut = count_values / 2;
		int i;

		if (count_values) {
			utf8 = (char **)malloc(sizeof(char **) *
					count_values + 1);
			memset(utf8, 0, sizeof(char **) *
					count_values + 1);
		}

		for (i = 0; i < count_values; i++) {
			tmpu16 = (UTFCHAR*)malloc(
				sizeof(UTFCHAR) *
				(aux->string_values[i].char_length + 1));
			if (!tmpu16)
				return;
			memset(tmpu16,
				0,
				sizeof(UTFCHAR) *
				(aux->string_values[i].char_length + 1));
			memcpy(tmpu16,
				aux->string_values[i].text.utf_chars,
				sizeof(UTFCHAR)*aux->string_values[i].char_length);
			utf8[i] = utf16be_to_utf8(NULL, tmpu16, NULL);
			free(tmpu16);
		}

		if (numWut) {
			wut = (WordUserTable *)malloc(numWut *
					sizeof(WordUserTable));
			memset(wut, 0, numWut * sizeof(WordUserTable));
		}
		for (i=0; i<numWut; i++) {
			wut[i].key = utf8[i*2];
			wut[i].word = utf8[i*2 + 1];
		}

		if (utf8)
			free(utf8);
		im_set_word_user_table(session, numWut, wut);
		break;
	}
#endif
	case UNIHAN_AUX_UNISPIM_SET: {
		int count_values = aux->count_integer_values - 1;
		int *unispim_values;
		int i;

		unispim_values = (int *)malloc(sizeof(int)*count_values);
		for(i=0; i<count_values; i++)
			unispim_values[i] = aux->integer_values[i+1];

		im_config_unispim(session, count_values,unispim_values);		
		break;	
	}

    case UNIHAN_ENGINE_CAND_COMMIT:
        if (aux->count_integer_values < 2)
            return;
        if (ds->phrase_state == PHRASE_STATE_SELECTING) {
            char *result = (char *)phrase_matcher_get_result(session, aux->integer_values[1]);
            if (result) {
                ds->phrase_state = PHRASE_STATE_IDLE;
                phrase_matcher_hide_results(session);
                UnihanIM uim;
                uim.privateData = session;
                commit_string(&uim, result);
                match_phrase(&uim, result);
                if (ds->phrase_state == PHRASE_STATE_SELECTING)
                    phrase_matcher_show_results(session);
            }
            break;
        }
        if (im_propagate_lookup_select_event(session, aux->integer_values[1])) {
            if (ds->phrase_state == PHRASE_STATE_SELECTING)
                phrase_matcher_show_results(session);
        }
        break;

    case UNIHAN_ENGINE_CAND_PAGEUP:
        if (ds->phrase_state == PHRASE_STATE_SELECTING) {
            phrase_matcher_prev_page(session);
            break;
        }
        im_propagate_lookup_page_turn_event(session, -1);
        break;

    case UNIHAN_ENGINE_CAND_PAGEDOWN:
        if (ds->phrase_state == PHRASE_STATE_SELECTING) {
            phrase_matcher_next_page(session);
            break;
        }
        im_propagate_lookup_page_turn_event(session, 1);
        break;

    case UNIHAN_ENGINE_EXTRA_CANDS:
        if (aux->count_integer_values < 3)
            return;
        {
            int num_cands = aux->integer_values[2];
            char **cands = (char **)malloc(sizeof(char *) * num_cands);
            int i;
            for (i = 0; i < num_cands; ++i) {
                IMText *imtext = &(aux->string_values[i + 1]);
                tmpu16 = (UTFCHAR*)calloc(imtext->char_length + 1, sizeof(UTFCHAR));
                memcpy(tmpu16, imtext->text.utf_chars, imtext->char_length * sizeof(UTFCHAR));
                cands[i] = utf16be_to_utf8(NULL, tmpu16, NULL);
                free(tmpu16);
            }
            phrase_matcher_add_extra_cands(session, cands, num_cands, aux->integer_values[1]);

            if (num_cands && ds->phrase_state != PHRASE_STATE_NEVER) {
                ds->phrase_state = PHRASE_STATE_SELECTING;
                phrase_matcher_show_results(session);
            }
        }
        break;

    // FIXME!!!
    case UNIHAN_ENGINE_SET_ENCODING:
    default:
        break;
    }
    return;
}


static void
commit_string_u16(iml_session_t *session, UTFCHAR *u16, int len)
{
	IMText _imt, *imt = &_imt;
	iml_inst *lp, *rrv = NULL;

	memset(imt, 0, sizeof(IMText));
	imt->encoding = UTF16_CODESET;
	imt->text.utf_chars = u16;
	imt->char_length = len;
	imt->feedback = feedback_new(session, imt->char_length);
	lp = session->If->m->iml_make_commit_inst(session, imt);
	session->If->m->iml_link_inst_tail(&rrv, lp);
	session->If->m->iml_execute(session, &rrv);
}

// vi:ts=4:sw=4:nowrap:cin:expandtab
